package frontend.parser;

import frontend.lexer.Lexer;
import frontend.lexer.TKType;
import frontend.lexer.Token;

import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Queue;
import java.util.Stack;

/**
 * 预读和缓存token
 *
 * @author 杨凯伟
 * @date 2024/10/13
 */
class PreReadBuffer {
    private final Lexer lexer;
    private final int tokenBufLen;
    private final Token[] tokenBuf;//环形缓冲区
    private int currTokenPos = 0;
    private final Queue<Token> findBuffer = new ArrayDeque<>();//比缓冲区更早的token序列
    private Token preToken = null;
    private ArrayList<Token> readTokens;//记录findBuffer已经读取过的所有token
    private int readPtr;//对齐tokenBuf的末尾token
    private Stack<ArrayList<Token>> readTokensStack = new Stack<>(); // 用于保存回溯前的 readTokens
    private Stack<Integer> readPtrStack = new Stack<>();
    private Stack<Token> preTokenStack = new Stack<>();//对齐tokenBuf的末尾token

    public ArrayList<Token> getReadTokens() {
        return readTokens;
    }

    public void setReadTokens(ArrayList<Token> readTokens) {
        this.readTokens = readTokens;
    }

    /**
     * @param lexer  词法分析器
     * @param bufLen 预读取几个Token
     */
    public PreReadBuffer(Lexer lexer, int bufLen) throws IOException {
        assert bufLen >= 2;

        this.lexer = lexer;
        this.tokenBufLen = bufLen;
        this.tokenBuf = new Token[this.tokenBufLen];
        this.readTokens = new ArrayList<>();
        this.readPtr = 0;
        for (int i = 1; i < this.tokenBufLen; i++) {
            if (this.lexer.next()) {
                this.tokenBuf[i] = lexer.getToken();
                readTokens.add(this.tokenBuf[i]);
                readPtr++;//与tokenBuf的末尾齐平
            } else {
                this.tokenBuf[i] = new Token(null, "EOF", 0);  // token not null
            }
        }
        readPtr--;//因为readTokens[0]也有效
    }

    public Token readNextToken() throws IOException {
        preToken = tokenBuf[currTokenPos];
        if (readPtr + 1 < readTokens.size()) {//被回溯过，上次读的token还没用完
            tokenBuf[currTokenPos] = readTokens.get(readPtr + 1);
        } else if (lexer.next()) {//findbuffer和readTokens都用完了，读新的
            tokenBuf[currTokenPos] = lexer.getToken();
            readTokens.add(tokenBuf[currTokenPos]);
        } else {
            tokenBuf[currTokenPos] = new Token(null, "EOF", 0);  // token not null
        }
        currTokenPos = (currTokenPos + 1) % tokenBufLen;
        readPtr++;//与tokenBuf最后一位对齐
        return tokenBuf[currTokenPos];
    }

    public Token readTokenWithOffset(int offset) {
        assert offset < tokenBufLen;
        return tokenBuf[(currTokenPos + offset) % tokenBufLen];
    }

    /**
     * 在预读缓冲区中查找指定的词法单元类型，直到遇到另一个指定的词法单元类型。
     *
     * @param find  指定类型
     * @param until 指定截止类型
     * @return boolean
     * @throws IOException
     */
    public boolean findUntil(TKType find, TKType until) throws IOException {
        for (int i = 0, j = currTokenPos; i < tokenBufLen; i++, j = (currTokenPos + i) % tokenBufLen) {//查找缓冲区---3个
            if (tokenBuf[j].getType() == find) {
                return true;
            } else if (tokenBuf[j].getType() == until) {
                return false;
            }
        }
        int findPtr = readPtr;
        while (findPtr < readTokens.size()) {//回溯后read的还没用完，转移到findbuffer
            var token = readTokens.get(findPtr++);
            findBuffer.add(token);
            if (token.getType() == find) {
                return true;
            } else if (token.getType() == until) {
                return false;
            }
        }
        while (lexer.next()) {//读入剩下的lexer,知道遇到until返回false,遇到find返回true
            var token = lexer.getToken();
            findBuffer.add(lexer.getToken());
            readTokens.add(lexer.getToken());//readTokens和findbuffer对齐
            if (token.getType() == find) {
                return true;
            } else if (token.getType() == until) {
                return false;
            }
        }
        return false;
    }

    // 新增方法：保存当前的 readTokens
    public void saveReadTokens() {
        readTokensStack.push(new ArrayList<>(readTokens));
        readPtrStack.push(readPtr);
        preTokenStack.push(preToken);
    }

    // 新增方法：恢复保存的 readTokens
    public void restoreReadTokens() {
//        System.err.println("开始回溯~");
//        readTokens = readTokensStack.pop();
        readPtr = readPtrStack.pop();
        preToken = preTokenStack.pop();

        findBuffer.clear();
        currTokenPos = 0;
        tokenBuf[0] = readTokens.get(readPtr - 2);
        tokenBuf[1] = readTokens.get(readPtr - 1);
        tokenBuf[2] = readTokens.get(readPtr);
    }

    /**
     * 返回前一个词法单元
     *
     * @return {@code Token}
     */
    public Token readPreToken() {
        return preToken;
    }
}
